package com.example.pattern.singleton;

import java.util.Objects;

/**
 * 单例设计模式  懒汉式
 */
public class LazySingletonTest {

    public static void main(String[] args) {
        new Thread(() -> {
            LazySingleton instance = LazySingleton.getInstance();
            System.out.println(instance);
        }).start();
        new Thread(() -> {
            LazySingleton instance = LazySingleton.getInstance();
            System.out.println(instance);
        }).start();

    }


}

/**
 * 懒汉式需要考虑的问题
 *  1、需要考虑多线程的情况，用 synchronized 关键字加锁
 *      如果 synchronized 关键字加到方法上，会影响性能，每个线程都会锁住，没有必要，如果instance 已经实例化，则不必要加锁了
 *  2、double check
 *      用 synchronized 加锁 也有可能出现多个线程同时拿到锁的情况, 需要判断两次
 *  3、考虑指令重排序
 *      new LazySingleton() jvm 字节码层 分为几个步骤
 *          1、 分配空间
 *          2、 初始化
 *          3、 引用赋值
 *      如果 JIM or CPU 对指令进行了重排序，颠倒了2 3 步骤，变成了 3 2，那么，在 T1线程刚好引用赋值完，T2线程就拿到了这个引用，那么就会发生空指针异常
 *      所以 用 volatile 来避免指令重排序
 */
class LazySingleton{
    private volatile static LazySingleton instance;
    private LazySingleton(){}

    public static LazySingleton getInstance(){
        if(Objects.isNull(instance)){
            //多个线程在这里并发
            synchronized (LazySingleton.class){
                //同时有两个线程拿到锁
                if(Objects.isNull(instance)) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }

}
